package com.example.teesystem.service.impl;

import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.teesystem.common.cache.UserCache;
import com.example.teesystem.common.utils.*;
import com.example.teesystem.entity.*;
import com.example.teesystem.entity.vo.Estimate.EstimateByCourseVo;
import com.example.teesystem.entity.vo.PageResponseVo;
import com.example.teesystem.entity.vo.course.CourseResponseVo;
import com.example.teesystem.entity.vo.user.UserSelectVo;
import com.example.teesystem.entity.vo.user.UserSelectVo2;
import com.example.teesystem.entityDto.*;
import com.example.teesystem.filter.MyHandlerInterceptor;
import com.example.teesystem.mapper.*;
import com.example.teesystem.service.CourseService;
import com.example.teesystem.service.ExamPaperService;
import com.example.teesystem.service.UserService;
import io.swagger.models.auth.In;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


/**
* @author 12510
* @description 针对表【t_user】的数据库操作Service实现
* @createDate 2022-03-23 09:49:07
*/
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{

    private final UserMapper userMapper;
    private final ExamEvaluationMapper examEvaluationMapper;
    private final IssueRecordMapper issueRecordMapper;
    private final ViolationMapper violationMapper;
    private final ExamPaperMapper examPaperMapper;
    private final IssueMapper issueMapper;
    private final RadarStatisticMapper radarStatisticMapper;
    private final AdvancedTrainingProgressMapper advancedTrainingProgressMapper;
    private final AdvancedTrainingMapper advancedTrainingMapper;

    private final CourseService courseService;
    private final ExamPaperService examPaperService;

    private static int ERROR = -1;



    @Override
    public String getSaltByUid(int uid) {
        String salt = RandomUtil.getRandomString(6);
        //账户被锁定时创建盐信息失败
        if(!LoginUtil.setSaltByUid(uid,salt)) {
            return "";
        }
        return salt;
    }

    @Override
    public boolean login(int uid, String passwordMD5) {
        String password="";
        password = UserCache.userMap.get(uid).getPassword();
        return LoginUtil.login(uid,password,passwordMD5);
    }

    @Override
    public PageResponseVo<UserList> getUser(UserSelectVo userSelectVo){
        Page<User> page = new Page<>(userSelectVo.getCur(), userSelectVo.getSize());
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq(userSelectVo.getAccountType()!=null,"account_type",userSelectVo.getAccountType())
                .eq(userSelectVo.getType()!=null,"type",userSelectVo.getType());
        Page<User> myPage = this.page(page,wrapper);
        List<UserList> users = myPage.getRecords().stream()
                .map(UserList::new)
                .collect(Collectors.toList());
        return new PageResponseVo<>(users, myPage.getCurrent(), myPage.getSize(), myPage.getPages());
    }

    @Override
    public Response getUserMsg(int id) {
        User user = null;
        user = userMapper.selectById(id);
        if(user == null){
            return Response.error(ResultCode.NO_USER.getMsg(), ResultCode.NO_USER.getCode());
        }else {
            return Response.ok(new UserMessage(user));
        }
    }

    @Override
    public Response getUserByMsg(UserSelectVo2 userSelectVo2) {
        Page<User> page = new Page<>(userSelectVo2.getCur(),userSelectVo2.getSize());
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq(userSelectVo2.getAccount()!=null,"account",userSelectVo2.getAccount())
                .eq(userSelectVo2.getMyName()!=null,"my_name",userSelectVo2.getMyName())
                .eq(userSelectVo2.getAcademy()!=null,"academy",userSelectVo2.getAcademy());
        Page<User> myPage = this.page(page,wrapper);
        List<User> userList = myPage.getRecords();
        List<UserList> userLists = new ArrayList<>();

        if(userList == null){
            return Response.error(ResultCode.NO_USER.getMsg(), ResultCode.NO_USER.getCode());
        }else {
            for (User user : userList) {
                userLists.add(new UserList(user));
            }
        }
        return Response.ok(userLists);
    }

    @Override
    public Response updateUserAccountType(Integer uid, Integer accountType) {
        if(uid == null || accountType == null){
            return Response.error(ResultCode.PARAMETER_MISS.getMsg(), ResultCode.PARAMETER_MISS.getCode());
        }
        User u = UserCache.userMap.get(uid);
        if(u == null){
            return Response.error(ResultCode.PARAMETER_ERROR.getMsg(), ResultCode.PARAMETER_ERROR.getCode());
        }
        userMapper.updateAccountTypeById(uid,accountType);
        u.setAccountType(accountType);
        UserCache.updateUser(uid,u);
        return Response.ok();
    }

    @Override
    public Response updateUserPassword(Integer uid, String password) {
        User u = UserCache.userMap.get(uid);
        if(u == null){
            return Response.error(ResultCode.PARAMETER_ERROR.getMsg(), ResultCode.PARAMETER_ERROR.getCode());
        }
        userMapper.updatePasswordById(uid, password);
        u.setPassword(password);
        UserCache.updateUser(uid,u);
        return Response.ok();
    }

    @Override
    public Response getUidByAccount(List<String> list) {
        Collection<User> userList = UserCache.userMap.values();
        List<UserIdAccount> userIdAccounts = new ArrayList<>();
        for(User user : userList){
            for (String s : list) {
                if (s.equals(user.getAccount())) {
                    userIdAccounts.add(new UserIdAccount(user.getId(),user.getAccount(),user.getMyName()));
                }
            }
        }
        return Response.ok(userIdAccounts);
    }


    @Override
    public int getUIdByAccount(String account) {
        try{
            return userMapper.getUidByAccount(account);
        }catch (Exception e){
            return ERROR;
        }
    }

    @Override
    public String getToken(User user) {
        String token = TokenUtil.getToken(user);
        //账户被锁定时创建盐信息失败
        AuthenticationUtil.addInfo(user.getId(),token);
        return token;
    }

    @Override
    public void logout(int uid) {
        AuthenticationUtil.deleteInfoByUid(uid);
    }

    @Override
    public Response register(User user) {
        int uid = -1;
        uid = this.getUIdByAccount(user.getAccount());
        if(user.getType() != 1 || user.getType() != 2){
            return Response.error(ResultCode.USER_TYPE_ERROR);
        }
        if(uid!=ERROR){
            return Response.error(ResultCode.USER_EXIST.getMsg(), ResultCode.USER_EXIST.getCode());
        }
        if(!Validator.isEmail(user.getMail())){
            return Response.error(ResultCode.EMAIL_ERROR.getMsg(), ResultCode.EMAIL_ERROR.getCode());
        }
        if(user.getAccount().length()>15||user.getAccount().length()<5){
            return Response.error(ResultCode.ACCOUNT_ERROR.getMsg(), ResultCode.ACCOUNT_ERROR.getCode());
        }
        if(user.getMyName().length()>10||user.getMyName().length()<2){
            return Response.error(ResultCode.NAME_ERROR.getMsg(), ResultCode.NAME_ERROR.getCode());
        }
        if(user.getUsername().length()>10||user.getUsername().length()<3){
            return Response.error(ResultCode.USER_NAME_ERROR.getMsg(), ResultCode.USER_NAME_ERROR.getCode());
        }
        int id = ERROR;
        userMapper.insert(user);
        id = user.getId();
        if(id == ERROR){
            return Response.error(ResultCode.INSERT_ERROR.getMsg(), ResultCode.INSERT_ERROR.getCode());
        }else {
            UserCache.userMap.put(user.getId(),user);
            System.out.println(UserCache.userMap);
            return Response.ok();
        }
    }

    @Override
    public Response updateMyMessage(String username, String gender, Date birthday, String name, String academy, int grade, int classes, String mail,int uid) {
        if(!Validator.isEmail(mail)){
            return Response.error(ResultCode.EMAIL_ERROR.getMsg(), ResultCode.EMAIL_ERROR.getCode());
        }
        if(name.length()>10||name.length()<2){
            return Response.error(ResultCode.NAME_ERROR.getMsg(), ResultCode.NAME_ERROR.getCode());
        }
        if(username.length()>10||username.length()<3){
            return Response.error(ResultCode.USER_NAME_ERROR.getMsg(), ResultCode.USER_NAME_ERROR.getCode());
        }
        try{
            userMapper.updateMyMessage(username,gender,birthday,name,academy,grade,classes,mail,uid,new Date(System.currentTimeMillis()));
            UserCache.userMap.put(uid,userMapper.selectById(uid));
            System.out.println(UserCache.userMap);
            return Response.ok();
        }catch (Exception e){
            System.out.println(e.getMessage());
            return Response.error(ResultCode.UPDATE_MESSAGE_ERROR.getMsg(), ResultCode.UPDATE_MESSAGE_ERROR.getCode());
        }
    }

    @Override
    public Response updatePassword(String oldPassword,String newPassword) {
        if(!UserCache.userMap.get(MyHandlerInterceptor.getUid()).getPassword().equals(oldPassword)) {
            return Response.error(ResultCode.PASSWORD_ERROR.getMsg(), ResultCode.PASSWORD_ERROR.getCode());
        }
        try{
            userMapper.updatePassword(newPassword,MyHandlerInterceptor.getUid());
            User u = UserCache.userMap.get(MyHandlerInterceptor.getUid());
            u.setPassword(newPassword);
            UserCache.updateUser(MyHandlerInterceptor.getUid(),u);
            return Response.ok();
        }catch (Exception e){
            System.out.println(e.getMessage());
            return Response.error(ResultCode.UPDATE_MESSAGE_ERROR.getMsg(), ResultCode.UPDATE_MESSAGE_ERROR.getCode());
        }
    }

    @Override
    public User getMyMessage(String account, String password) {
        return null;
    }

    public Response getEstimateOfStudent(int studentId){
        Estimate estimate = new Estimate(studentId);
        if(courseService.getCourseByStudentId(studentId).size() !=0 && issueRecordMapper.getStudentRecorde(studentId).size() != 0){
            estimate.setCourses(courseService.getCourseByStudentId(studentId));
            estimate.setCorrectRate((float) issueRecordMapper.getCorrectNum(studentId) / (float) (issueRecordMapper.getCorrectNum(studentId) + issueRecordMapper.getWrongNum(studentId)));
            estimate.setViolationNum(violationMapper.getViolationByStudentId(studentId).size());

            //将得分拉到80后的平均成绩，加上总分为20的难度系数作为最终的平均成绩
            List<ExamEvaluation> examEvaluations = examEvaluationMapper.getStudentMarkedExamEvaluation(studentId);
            int num = examEvaluations.size();
            float sum = 0;
            for(ExamEvaluation examEvaluation : examEvaluations){
                int hardLevel = 4 * (examPaperMapper.selectById(examEvaluation.getPaperId()).getLevel());
                sum = sum + examPaperService.getScorePropertyByPaperId(examEvaluation.getPaperId(),studentId) * 8 / 10 + hardLevel;
            }
            estimate.setMeanScore(sum / num);
            float examScore = 0;
            //先将分数映射到1-5之间
            if(estimate.getMeanScore() < 60) {
                examScore = 0;
            } else {
                examScore = (estimate.getMeanScore()-60) / 20;//2
            }
            float issueScore = estimate.getCorrectRate() * 2;//2
            float judgeScore = examScore + issueScore;
            //无作弊情况则补全最后一分老实分
            if(estimate.getViolationNum() == 0) {
                judgeScore += 1;
            } else {
                judgeScore += 1 - EstimateGenerate.sigmoidF(estimate.getViolationNum());
            }
            //调用工具类得到最终评价的map
            estimate.setLevel(EstimateGenerate.LevelJudge(judgeScore));
            return Response.ok(estimate);
        }
        else {
            estimate.setCourses(null);
            estimate.setCorrectRate(0);
            estimate.setViolationNum(0);
            estimate.setMeanScore(0);
            estimate.setLevel(EstimateGenerate.LevelJudge(-1));
            return Response.ok(estimate);
        }
    }

    //学生的答题正确率按照课程号一一对应
    public Map<Integer, Float> CourseIssueCorrectRate(int studentId){
        List<CourseResponseVo> courseResponseVos = courseService.getCourseByStudentId(studentId);
        Map<Integer, List<Issue>> CourseIssueMap = new HashMap<>();
        Map<Integer, Float> CourseIssueCorrectRateMap = new HashMap<>();
        //每门课程一个map
        List<IssueRecord> issueRecords = issueRecordMapper.getStudentRecorde(studentId);
        List<Integer> issueIds = new ArrayList<>();
        //取得学生完成的所有的题目issueId
        for(IssueRecord issueRecord : issueRecords){
            issueIds.add(issueRecord.getIssueId());
        }
        //学生上的每门课获取答题情况
        for(CourseResponseVo courseResponseVo : courseResponseVos){
            Integer courseId = courseResponseVo.getId();
            List<Issue> issuesOfCourse = issueMapper.getIssueByCourseId(courseId);
            List<Issue> issuesOfCourseByStudentId = new ArrayList<>();
            //获取某课程下的学生完成的所有题目
            for(Integer id : issueIds){
                if(issuesOfCourse.contains(issueMapper.selectById(id))){
                    issuesOfCourseByStudentId.add(issueMapper.selectById(id));
                }
            }
            CourseIssueMap.put(courseId, issuesOfCourseByStudentId);
        }
        //计算某学生每门课下的正确率
        for(CourseResponseVo courseResponseVo1 : courseResponseVos){
            List<Issue> issuesOfCourse = CourseIssueMap.get(courseResponseVo1.getId());
            float right_num = 0;
            float wrong_num = 0;
            float correctRate;
            for(Issue issue : issuesOfCourse){
                IssueRecord issueRecord = issueRecordMapper.getRecordByIssueIdAndStudentId(issue.getId(), studentId);
                right_num += issueRecord.getRightNum();
                wrong_num += issueRecord.getWrongNum();
            }
            if(right_num == 0 && wrong_num == 0){
                correctRate = -1;
            }
            else {
                correctRate = right_num / (right_num + wrong_num);
            }
            CourseIssueCorrectRateMap.put(courseResponseVo1.getId(), correctRate);
        }
        return CourseIssueCorrectRateMap;
    }

    public Map<Integer, Float> CourseMeanScore(int studentId){
        List<CourseResponseVo> courseResponseVos = courseService.getCourseByStudentId(studentId);
        Map<Integer, List<ExamPaper>> CoursePaperMap = new HashMap<>();
        Map<Integer, Float> courseMeanScore = new HashMap<>();

        //学生考试情况
        List<ExamEvaluation> examEvaluations = examEvaluationMapper.getStudentMarkedExamEvaluation(studentId);
        //获取某学生考试所用试卷Id
        List<Integer> paperIds = new ArrayList<>();
        for( ExamEvaluation examEvaluation : examEvaluations){
            paperIds.add(examEvaluation.getPaperId());
        }

        for(CourseResponseVo courseResponseVo : courseResponseVos){
            Integer courseId = courseResponseVo.getId();
            List<ExamPaper> examPapersOfCourse = examPaperMapper.getExamPaperByCourseId(courseId);
            List<ExamPaper> examPaperOfCourseByStudent = new ArrayList<>();

            for(Integer id : paperIds){
                if(examPapersOfCourse.contains(examPaperMapper.selectById(id))){
                    examPaperOfCourseByStudent.add(examPaperMapper.selectById(id));
                }
            }
            CoursePaperMap.put(courseId, examPaperOfCourseByStudent);
        }

        for(CourseResponseVo courseResponseVo : courseResponseVos){
            List<ExamPaper> examPapers = CoursePaperMap.get(courseResponseVo.getId());
            float sum = 0;
            float sum_all = 0;
            Float meanScore;
            for(ExamPaper examPaper : examPapers){
                ExamEvaluation examEvaluation = examEvaluationMapper.getEvaluationByPaperAndUserId(examPaper.getId(), studentId);
                sum += examEvaluation.getScore();
                sum_all += examPaper.getScore();
            }
            if(sum_all == 0){
                meanScore =(float)(-1);
            }
            else{
                meanScore = (sum / sum_all) * 100;
            }
            courseMeanScore.put(courseResponseVo.getId(), meanScore);
        }
        return courseMeanScore;
    }

    public Response GetStudentEstimateByCourses(int studentId){
        EstimateByCourseVo estimateByCourseVo = new EstimateByCourseVo(studentId);

        Map<Integer, Map<Integer,String>> levelsByCourseMap = new HashMap<>();

        if(courseService.getCourseByStudentId(studentId).size() !=0 && issueRecordMapper.getStudentRecorde(studentId).size() != 0) {
            estimateByCourseVo.setCourses(courseService.getCourseByStudentId(studentId));
            estimateByCourseVo.setViolationNum(violationMapper.getViolationByStudentId(studentId).size());
            estimateByCourseVo.setCourseCorrectRate(CourseIssueCorrectRate(studentId));
            estimateByCourseVo.setCourseMeanScore(CourseMeanScore(studentId));
            for (CourseResponseVo courseResponseVo : estimateByCourseVo.getCourses()) {
                Integer courseId = courseResponseVo.getId();
                float correctRate = estimateByCourseVo.getCourseCorrectRate().get(courseId);
                float meanScore = estimateByCourseVo.getCourseMeanScore().get(courseId);

                if (correctRate != -1 && meanScore != -1) {
                    float examScore = 0;
                    float issueScore = 0;
                    if (meanScore < 60) {
                        examScore = 0;
                    } else {
                        examScore = (meanScore - 60) / 20;
                    }

                    if (correctRate != -1) {
                        issueScore = correctRate * 2;
                    }
                    else {
                        issueScore = 0;
                    }
                    float judgeScore = examScore + issueScore + 1;
                    List<Violation> violations = violationMapper.getViolationByStudentId(studentId);
                    for (Violation violation : violations) {
                        for ( ExamPaper e :  examPaperMapper.getExamPaperByCourseId(courseId)) {
                            if(Objects.equals(e.getId(), violation.getPaperId())) {
                                judgeScore = judgeScore - 1;
                                break;
                            }
                            if(judgeScore < 1){
                                break;
                            }
                        }
                    }
                    levelsByCourseMap.put(courseId, EstimateGenerate.LevelJudge(judgeScore));
                } else {
                    levelsByCourseMap.put(courseId, EstimateGenerate.LevelJudge(-1));
                }
            }
            estimateByCourseVo.setLevel(levelsByCourseMap);
            return Response.ok(estimateByCourseVo);
        }
        else {
            estimateByCourseVo.setCourses(null);
            estimateByCourseVo.setCourseCorrectRate(null);
            estimateByCourseVo.setCourseMeanScore(null);
            estimateByCourseVo.setViolationNum(0);
            estimateByCourseVo.setLevel(null);
            return Response.ok(estimateByCourseVo);
        }
    }

    @Override
    public void updateAvatar(String avatarUrl) {
        userMapper.updateAvatarUrl(MyHandlerInterceptor.getUid(),avatarUrl);
        User user = UserCache.userMap.get(MyHandlerInterceptor.getUid());
        user.setAvatarUrl(avatarUrl);
        UserCache.userMap.put(MyHandlerInterceptor.getUid(),user);
    }

    @Override
    public void loginSuccess(int id) {
        userMapper.loginSuccess(id);
    }

    @Override
    public Integer getLoginScore() {
        int count = userMapper.getUserLoginCount(MyHandlerInterceptor.getUid());
        if((count>=0) && (count<20)) {
            return 5;
        } else if((count>=20) && (count< 40)) {
            return 6;
        } else if((count>=40) && (count< 60)) {
            return 7;
        } else if((count>=60) && (count< 80)) {
            return 8;
        } else if((count>=80) && (count< 100)) {
            return 9;
        } else {
            return 10;
        }
    }

    @Override
    public Response studentEvaluate(int courseId, Date startTime, Date endTime) {
        List<RadarStatistic> radarStatistics = radarStatisticMapper.getStudentRadarStatistic(courseId,MyHandlerInterceptor.getUid(),startTime,endTime);
        StudentAnswerMessage studentAnswerMessage = issueRecordMapper.getStudentAnswerMessage(courseId,MyHandlerInterceptor.getUid());
        TrainingStatistic trainingStatistic = new TrainingStatistic();
        StudentEvaluate studentEvaluate = new StudentEvaluate();

        studentAnswerMessage.setTotal(studentAnswerMessage.getRight()+studentAnswerMessage.getWrong());

        int allNum=0,num = 0;
        if(advancedTrainingMapper.getAllNum(courseId)!=null) {
            allNum = advancedTrainingMapper.getAllNum(courseId);
        }

        trainingStatistic.setAllNum(allNum);

        if(advancedTrainingProgressMapper.getAnswerNum(courseId,MyHandlerInterceptor.getUid())!=null){
            num = advancedTrainingProgressMapper.getAnswerNum(courseId,MyHandlerInterceptor.getUid());
        }
        trainingStatistic.setTrainingNum(num);

        User user = userMapper.selectById(MyHandlerInterceptor.getUid());

        studentEvaluate.setRadarStatistics(radarStatistics);
        studentEvaluate.setStudentAnswerMessage(studentAnswerMessage);
        studentEvaluate.setTrainingStatistic(trainingStatistic);
        studentEvaluate.setLoginCount(user.getCount());

        return Response.ok(studentEvaluate);
    }


}




